#ifndef THREAD_POOL_HPP
# define THREAD_POOL_HPP

#ifdef DEBUG
    #include <cstdio>
    #include <iostream>
#endif

#include <thread>
#include <atomic>
#include <vector>
#include <functional>
#include "locked_thread_safe_queue.hpp"


class join_threads
{
    std::vector<std::thread>& threads;
public:
    explicit join_threads(std::vector<std::thread>& threads_):
    threads(threads_) {}
    ~join_threads()
    {
        for(unsigned long i=0; i < threads.size(); ++i)
        {
            if(threads[i].joinable())
                threads[i].join();
        }
    }
};

class thread_pool
{
    // done标志和worker_queue必须在threads数组之前声明，而threads数组必须在joiner前声明，这样就能确保成员以正确的顺序析构
    std::atomic_bool done;
    thread_safe_queue<std::function<void()>> work_queue;
    std::vector<std::thread> threads;
    join_threads joiner;

    #ifdef DEBUG
        int task_count;
    #endif

    void worker_thread()
    {
        while(!done)
        {
            std::function<void()> task;
            if( work_queue.wait_pop(task) )
            {
                task();
            }
            else
            {
                std::this_thread::yield();
            }
        }
    }

public:
    thread_pool(): done(false), joiner(threads)
    {
        unsigned const thread_count = std::thread::hardware_concurrency();

        #ifdef DEBUG
            this->task_count = 0;
            std::cout << "Hardware Concurrency : " << thread_count << std::endl;
        #endif

        try
        {
            for( unsigned i = 0; i < thread_count; ++i )
            {
                // 新建线程
                threads.push_back( std::thread( &thread_pool::worker_thread, this ) );
            }
        }
        catch(...)
        {
            done = true;  // 10
            throw;
        }
    }

    ~thread_pool()
    {
        done=true;  // 11
    }

    template<typename FunctionType>
    void submit(FunctionType f)
    {
        #ifdef DEBUG
            this->task_count += 1;
            std::cout << task_count << "th Task submitted!" << std::endl;
        #endif

        work_queue.push(std::function<void()>(f));  // 12
    }
};

#endif